/*
* Copyright (c) 2007-2010 SlimDX Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

/*

Sig ::= MethodDefSig | MethodRefSig | StandAloneMethodSig | FieldSig | PropertySig | LocalVarSig

MethodDefSig ::= [[HASTHIS] [EXPLICITTHIS]] (DEFAULT|VARARG|GENERIC GenParamCount) ParamCount RetType Param*

MethodRefSig ::= [[HASTHIS] [EXPLICITTHIS]] VARARG ParamCount RetType Param* [SENTINEL Param+]

StandAloneMethodSig ::=  [[HASTHIS] [EXPLICITTHIS]] (DEFAULT|VARARG|C|STDCALL|THISCALL|FASTCALL) 
ParamCount RetType Param* [SENTINEL Param+]

FieldSig ::= FIELD CustomMod* Type

PropertySig ::= PROPERTY [HASTHIS] ParamCount CustomMod* Type Param*

LocalVarSig ::= LOCAL_SIG Count (TYPEDBYREF | ([CustomMod] [Constraint])* [BYREF] Type)+ 


-------------

CustomMod ::= ( CMOD_OPT | CMOD_REQD ) ( TypeDefEncoded | TypeRefEncoded )

Constraint ::= #define ELEMENT_TYPE_PINNED

Param ::= CustomMod* ( TYPEDBYREF | [BYREF] Type )

RetType ::= CustomMod* ( VOID | TYPEDBYREF | [BYREF] Type )

Type ::= ( BOOLEAN | CHAR | I1 | U1 | U2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I | U |
| VALUETYPE TypeDefOrRefEncoded
| CLASS TypeDefOrRefEncoded
| STRING 
| OBJECT
| PTR CustomMod* VOID
| PTR CustomMod* Type
| FNPTR MethodDefSig
| FNPTR MethodRefSig
| ARRAY Type ArrayShape
| SZARRAY CustomMod* Type
| GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type*
| VAR Number
| MVAR Number

ArrayShape ::= Rank NumSizes Size* NumLoBounds LoBound*

TypeDefOrRefEncoded ::= TypeDefEncoded | TypeRefEncoded
TypeDefEncoded ::= 32-bit-3-part-encoding-for-typedefs-and-typerefs
TypeRefEncoded ::= 32-bit-3-part-encoding-for-typedefs-and-typerefs

ParamCount ::= 29-bit-encoded-integer
GenArgCount ::= 29-bit-encoded-integer
Count ::= 29-bit-encoded-integer
Rank ::= 29-bit-encoded-integer
NumSizes ::= 29-bit-encoded-integer
Size ::= 29-bit-encoded-integer
NumLoBounds ::= 29-bit-encoded-integer
LoBounds ::= 29-bit-encoded-integer
Number ::= 29-bit-encoded-integer

*/

#include "stdafx.h"

#include "SigParse.h"

namespace SlimGen 
{
	bool SigParser::Parse(sig_byte const *pb, sig_count cbBuffer)
	{
		pbBase = pb;
		pbCur = pb;
		pbEnd = pbBase + cbBuffer;

		sig_elem_type elem_type;

		if (!ParseByte(&elem_type))
			return false;

		switch (elem_type & 0xf)
		{
		case SIG_METHOD_DEFAULT:  // default calling convention
		case SIG_METHOD_C:    // C calling convention
		case SIG_METHOD_STDCALL: // Stdcall calling convention
		case SIG_METHOD_THISCALL: // thiscall  calling convention
		case SIG_METHOD_FASTCALL: // fastcall calling convention
		case SIG_METHOD_VARARG:   // vararg calling convention
			return ParseMethod(elem_type);
			break;

		case SIG_FIELD:           // encodes a field
			return ParseField(elem_type);
			break;

		case SIG_LOCAL_SIG:       // used for the .locals directive
			return ParseLocals(elem_type);
			break;

		case SIG_PROPERTY:        // used to encode a property
			return ParseProperty(elem_type);
			break;

		default:
			// unknown signature
			break;
		}

		return false;
	}

	bool SigParser::ParseSpec(sig_byte const*, sig_count)
	{
		return false;
	}

	bool SigParser::ParseByte(sig_byte* pbOut)
	{
		if (pbCur < pbEnd)
		{
			*pbOut = *pbCur;
			pbCur++;
			return true;
		}

		return false;
	}


	bool SigParser::ParseMethod(sig_elem_type elem_type)
	{
		// MethodDefSig ::= [[HASTHIS] [EXPLICITTHIS]] (DEFAULT|VARARG|GENERIC GenParamCount)
		//                    ParamCount RetType Param* [SENTINEL Param+]

		NotifyBeginMethod(elem_type);

		sig_count gen_param_count;
		sig_count param_count;

		if (elem_type & SIG_GENERIC)
		{
			if (!ParseNumber(&gen_param_count))
				return false;

			NotifyGenericParamCount(gen_param_count);        
		}

		if (!ParseNumber(&param_count))
			return false;

		NotifyParamCount(param_count);

		if (!ParseRetType())
			return false;

		bool fEncounteredSentinal = false;

		for (sig_count i = 0; i < param_count; i++)
		{
			if (pbCur >= pbEnd)
				return false;

			if (*pbCur == ELEMENT_TYPE_SENTINEL)
			{
				if (fEncounteredSentinal)
					return false;

				fEncounteredSentinal = true;
				NotifySentinal();
				pbCur++;
			}

			if (!ParseParam())
				return false;
		}

		NotifyEndMethod();

		return true;
	}


	bool SigParser::ParseField(sig_elem_type elem_type)
	{
		// FieldSig ::= FIELD CustomMod* Type

		NotifyBeginField(elem_type);

		if (!ParseOptionalCustomMods())
			return false;

		if (!ParseType())
			return false;

		NotifyEndField();

		return true;
	}


	bool SigParser::ParseProperty(sig_elem_type elem_type)
	{
		// PropertySig ::= PROPERTY [HASTHIS] ParamCount CustomMod* Type Param*

		NotifyBeginProperty(elem_type);

		sig_count param_count;

		if (!ParseNumber(&param_count))
			return false;

		NotifyParamCount(param_count);

		if (!ParseOptionalCustomMods())
			return false;

		if (!ParseType())
			return false;

		for (sig_count i = 0; i < param_count; i++)
		{
			if (!ParseParam())
				return false;
		}

		NotifyEndProperty();

		return true;
	}


	bool SigParser::ParseLocals(sig_elem_type elem_type)
	{
		//   LocalVarSig ::= LOCAL_SIG Count (TYPEDBYREF | ([CustomMod] [Constraint])* [BYREF] Type)+ 

		NotifyBeginLocals(elem_type);

		sig_count local_count;

		if (!ParseNumber(&local_count))
			return false;

		NotifyLocalsCount(local_count);

		for (sig_count i = 0; i < local_count; i++)
		{
			if (!ParseLocal())
				return false;
		}

		NotifyEndLocals();

		return true;
	}


	bool SigParser::ParseLocal()
	{
		//TYPEDBYREF | ([CustomMod] [Constraint])* [BYREF] Type
		NotifyBeginLocal();

		if (pbCur >= pbEnd)
			return false;

		if (*pbCur == ELEMENT_TYPE_TYPEDBYREF)
		{
			NotifyTypedByref();
			pbCur++;
			goto Success;
		}

		if (!ParseOptionalCustomModsOrConstraint())
			return false;

		if (pbCur >= pbEnd)
			return false;

		if (*pbCur == ELEMENT_TYPE_BYREF)
		{
			NotifyByref();
			pbCur++;
		}

		if (!ParseType())
			return false;

Success:
		NotifyEndLocal();
		return true;
	}


	bool SigParser::ParseOptionalCustomModsOrConstraint()
	{  
		for (;;)
		{
			if (pbCur >= pbEnd)
				return true;

			switch (*pbCur)
			{
			case ELEMENT_TYPE_CMOD_OPT:
			case ELEMENT_TYPE_CMOD_REQD:
				if (!ParseCustomMod())
					return false;
				break;

			case ELEMENT_TYPE_PINNED:
				NotifyConstraint(*pbCur);
				pbCur++;
				break;

			default:
				return true;
			}
		}

		return false;
	}


	bool SigParser::ParseOptionalCustomMods()
	{
		for (;;)
		{
			if (pbCur >= pbEnd)
				return true;

			switch (*pbCur)
			{
			case ELEMENT_TYPE_CMOD_OPT:
			case ELEMENT_TYPE_CMOD_REQD:
				if (!ParseCustomMod())
					return false;
				break;       

			default:
				return true;
			}
		}

		return false;
	}



	bool SigParser::ParseCustomMod()
	{
		sig_elem_type cmod = 0;
		sig_index index;
		sig_index_type indexType;

		if (!ParseByte(&cmod))
			return false;

		if (cmod == ELEMENT_TYPE_CMOD_OPT || cmod == ELEMENT_TYPE_CMOD_REQD)
		{
			if (!ParseTypeDefOrRefEncoded(&indexType, &index))
				return false;

			NotifyCustomMod(cmod, indexType, index);
			return true;
		}

		return false;
	}


	bool SigParser::ParseParam()
	{
		// Param ::= CustomMod* ( TYPEDBYREF | [BYREF] Type )

		NotifyBeginParam();

		if (!ParseOptionalCustomMods())
			return false;

		if (pbCur >= pbEnd)
			return false;

		if (*pbCur == ELEMENT_TYPE_TYPEDBYREF)
		{
			NotifyTypedByref();
			pbCur++;
			goto Success;
		}

		if (*pbCur == ELEMENT_TYPE_BYREF)
		{
			NotifyByref();
			pbCur++;
		}

		if (!ParseType())
			return false;

Success:
		NotifyEndParam();
		return true;
	}


	bool SigParser::ParseRetType()
	{
		// RetType ::= CustomMod* ( VOID | TYPEDBYREF | [BYREF] Type )

		NotifyBeginRetType();

		if (!ParseOptionalCustomMods())
			return false;

		if (pbCur >= pbEnd)
			return false;

		if (*pbCur == ELEMENT_TYPE_TYPEDBYREF)
		{
			NotifyTypedByref();
			pbCur++;
			goto Success;
		}

		if (*pbCur == ELEMENT_TYPE_VOID)
		{
			NotifyVoid();
			pbCur++;
			goto Success;
		}

		if (*pbCur == ELEMENT_TYPE_BYREF)
		{
			NotifyByref();
			pbCur++;
		}

		if (!ParseType())
			return false;

Success:        
		NotifyEndRetType();
		return true;
	}

	bool SigParser::ParseArrayShape()
	{
		sig_count rank;
		sig_count numsizes;
		sig_count size;

		// ArrayShape ::= Rank NumSizes Size* NumLoBounds LoBound*
		NotifyBeginArrayShape();
		if (!ParseNumber(&rank))
			return false;

		NotifyRank(rank);

		if (!ParseNumber(&numsizes))
			return false;

		NotifyNumSizes(numsizes);

		for (sig_count i = 0; i < numsizes; i++)
		{
			if (!ParseNumber(&size))
				return false;

			NotifySize(size);
		}

		if (!ParseNumber(&numsizes))
			return false;

		NotifyNumLoBounds(numsizes);

		for (sig_count i = 0; i < numsizes; i++)
		{
			if (!ParseNumber(&size))
				return false;

			NotifyLoBound(size);
		}

		NotifyEndArrayShape();
		return true;    
	}

	bool SigParser::ParseType(unsigned int totalCount)
	{
		/*
		Type ::= ( BOOLEAN | CHAR | I1 | U1 | U2 | U2 | I4 | U4 | I8 | U8 | R4 | R8 | I | U |
		| VALUETYPE TypeDefOrRefEncoded
		| CLASS TypeDefOrRefEncoded
		| STRING 
		| OBJECT
		| PTR CustomMod* VOID
		| PTR CustomMod* Type
		| FNPTR MethodDefSig
		| FNPTR MethodRefSig
		| ARRAY Type ArrayShape
		| SZARRAY CustomMod* Type
		| GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *
		| VAR Number
		| MVAR Number

		*/

		NotifyBeginType();

		sig_elem_type elem_type;
		sig_index index;
		sig_mem_number number;
		sig_index_type indexType;

		if (!ParseByte(&elem_type))
			return false;

		switch (elem_type)
		{
		case  ELEMENT_TYPE_BOOLEAN:
		case  ELEMENT_TYPE_CHAR:
		case  ELEMENT_TYPE_I1:
		case  ELEMENT_TYPE_U1: 
		case  ELEMENT_TYPE_U2: 
		case  ELEMENT_TYPE_I2: 
		case  ELEMENT_TYPE_I4: 
		case  ELEMENT_TYPE_U4: 
		case  ELEMENT_TYPE_I8: 
		case  ELEMENT_TYPE_U8: 
		case  ELEMENT_TYPE_R4: 
		case  ELEMENT_TYPE_R8: 
		case  ELEMENT_TYPE_I:
		case  ELEMENT_TYPE_U:
		case  ELEMENT_TYPE_STRING:
		case  ELEMENT_TYPE_OBJECT:
			// simple types
			NotifyTypeSimple(elem_type);
			break;

		case  ELEMENT_TYPE_PTR:
			// PTR CustomMod* VOID
			// PTR CustomMod* Type

			NotifyTypePointer();

			if (!ParseOptionalCustomMods())
				return false;

			if (pbCur >= pbEnd)
				return false;

			if (*pbCur == ELEMENT_TYPE_VOID)
			{
				pbCur++;
				NotifyVoid();
				break;
			}

			if (!ParseType())
				return false;

			break;

		case  ELEMENT_TYPE_CLASS: 
			// CLASS TypeDefOrRefEncoded
			NotifyTypeClass();

			if (!ParseTypeDefOrRefEncoded(&indexType, &index))
				return false;

			NotifyTypeDefOrRef(indexType, index);      
			break;

		case  ELEMENT_TYPE_VALUETYPE: 
			//VALUETYPE TypeDefOrRefEncoded
			NotifyTypeValueType();

			if (!ParseTypeDefOrRefEncoded(&indexType, &index))
				return false;

			NotifyTypeDefOrRef(indexType, index);      
			break;

		case  ELEMENT_TYPE_FNPTR:
			// FNPTR MethodDefSig
			// FNPTR MethodRefSig
			NotifyTypeFunctionPointer();

			if (!ParseByte(&elem_type))
				return false;

			if (!ParseMethod(elem_type))
				return false;

			break;

		case  ELEMENT_TYPE_ARRAY:
			// ARRAY Type ArrayShape
			NotifyTypeArray();

			if (!ParseType())
				return false;

			if (!ParseArrayShape())
				return false;
			break;

		case  ELEMENT_TYPE_SZARRAY:
			// SZARRAY CustomMod* Type

			NotifyTypeSzArray();

			if (!ParseOptionalCustomMods())
				return false;

			if (!ParseType())
				return false;

			break;

		case  ELEMENT_TYPE_GENERICINST:
			// GENERICINST (CLASS | VALUETYPE) TypeDefOrRefEncoded GenArgCount Type *

			if (!ParseByte(&elem_type))
				return false;

			if (elem_type != ELEMENT_TYPE_CLASS && elem_type != ELEMENT_TYPE_VALUETYPE)
				return false;

			if (!ParseTypeDefOrRefEncoded(&indexType, &index))
				return false;

			if (!ParseNumber(&number))
				return false;

			NotifyTypeGenericInst(elem_type, indexType, index, number);

			{
				for (sig_mem_number i=0; i < number; i++)
				{
					if (!ParseType(number))
						return false;
				}
			}

			break;

		case  ELEMENT_TYPE_VAR:
			// VAR Number
			if (!ParseNumber(&number))
				return false;
			NotifyTypeGenericTypeVariable(number, totalCount);
			break;

		case  ELEMENT_TYPE_MVAR:
			// MVAR Number
			if (!ParseNumber(&number))
				return false;
			NotifyTypeGenericMemberVariable(number, totalCount);
			break;
		}

		NotifyEndType();

		return true;
	}

	bool SigParser::ParseTypeDefOrRefEncoded(sig_index_type *pIndexTypeOut, sig_index *pIndexOut)
	{
		// parse an encoded typedef or typeref

		sig_count encoded  = 0;

		if (!ParseNumber(&encoded))
			return false;

		*pIndexTypeOut = (sig_index_type) (encoded & 0x3);
		*pIndexOut = (encoded >> 2);
		return true;
	}

	bool SigParser::ParseNumber(sig_count *pOut)
	{
		// parse the variable length number format (0-4 bytes)

		sig_byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;

		// at least one byte in the encoding, read that

		if (!ParseByte(&b1))
			return false;

		if (b1 == 0xff)
		{
			// special encoding of 'NULL'
			// not sure what this means as a number, don't expect to see it except for string lengths
			// which we don't encounter anyway so calling it an error
			return false;
		}

		// early out on 1 byte encoding
		if ( (b1 & 0x80) == 0)
		{
			*pOut = (int)b1;
			return true;
		}

		// now at least 2 bytes in the encoding, read 2nd byte
		if (!ParseByte(&b2))
			return false;

		// early out on 2 byte encoding
		if ( (b1 & 0x40) == 0)
		{
			*pOut = (((b1 & 0x3f) << 8) | b2);
			return true;
		}

		// must be a 4 byte encoding

		if ( (b1 & 0x20) != 0)        
		{
			// 4 byte encoding has this bit clear -- error if not
			return false;
		} 

		if (!ParseByte(&b3))
			return false;

		if (!ParseByte(&b4))
			return false;

		*pOut = ((b1 & 0x1f)<<24) | (b2<<16) | (b3<<8) | b4;
		return true;
	}
}